#include "ncp_parse.h" 
#include "ncp_rule.h"
#include <map>
#include <string>
#include <sstream>
#include <iomanip>

#include "../logger.h"

using namespace std;

extern "C"
{
#include "ncp_log.h"
}

/* if enable map func */
static bool g_map_enable = false;

/* map table */
extern CRITICAL_SECTION critical_table;
typedef map<string, string> def_map_table;
static def_map_table g_map_table;

/* get ip mac table*/
char** map_get_table(int* count) 
{	
	*count                       = g_map_table.size();
	char** table                 = (char**)malloc(sizeof(char*) * *count);		
	def_map_table::iterator iter;
	int i                        = 0;

	EnterCriticalSection(&critical_table);  
	for(iter=g_map_table.begin(); iter!=g_map_table.end(); ++iter,++i)
	{
		table[i] = strdup(iter->first.c_str());
	}		
	LeaveCriticalSection(&critical_table);
	
	return table;
}

/* get mac by ip */
char* map_get_mac_by_ip(char* ip)
{
	if(NULL == ip)
	{
		return NULL;
	}

	string ipaddr = ip;
	char* res     = NULL;	

	EnterCriticalSection(&critical_table); 			
	def_map_table::iterator iter = g_map_table.find(ipaddr);

	if(g_map_table.end() != g_map_table.find(ipaddr))
	{
		res = strdup(iter->second.c_str());
	}
	
	LeaveCriticalSection(&critical_table);	
	return res;
}

/* enable ip mac table func */
void map_set_enable(bool enable)
{
	if(g_map_enable == enable)
	{
		return;
	}

	if(g_map_enable = enable)
	{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_ENABLE_MACIP_TABLE);	
#endif		
	}
	else
	{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_DISABLE_MACIP_TABLE);	
#endif		
	}
}

/* clear map table */
void map_clear_table()
{
	g_map_table.clear();
}


#if LOG_LEVEL_FIV >= LOG_CUR_LEVEL
void parse_draw_packet(def_packet_struct* packet_struct)
{
	//printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);
	fprintf(g_log_io, LOG_DRAW_PACKET_START);
	if(NULL != packet_struct->ether_header)
	{
		fprintf(g_log_io,
			LOG_DRAW_ETHER_PACKET,
			packet_struct->ether_header->type,
			packet_struct->ether_header->saddr.char1,
			packet_struct->ether_header->saddr.char2,
			packet_struct->ether_header->saddr.char3,
			packet_struct->ether_header->saddr.char4,
			packet_struct->ether_header->saddr.char5,
			packet_struct->ether_header->saddr.char6,
			packet_struct->ether_header->daddr.char1,
			packet_struct->ether_header->daddr.char2,
			packet_struct->ether_header->daddr.char3,
			packet_struct->ether_header->daddr.char4,
			packet_struct->ether_header->daddr.char5,
			packet_struct->ether_header->daddr.char6);
	}

	if(NULL != packet_struct->ip_header)
	{
		fprintf(g_log_io,
			LOG_DRAW_IP_PACKET,
			packet_struct->ip_header->proto,
			packet_struct->ip_header->saddr.char1,
			packet_struct->ip_header->saddr.char2,
			packet_struct->ip_header->saddr.char3,
			packet_struct->ip_header->saddr.char4,
			packet_struct->ip_header->daddr.char1,
			packet_struct->ip_header->daddr.char2,
			packet_struct->ip_header->daddr.char3,
			packet_struct->ip_header->daddr.char4);
	}

	if(NULL != packet_struct->tcp_header)
	{
		fprintf(g_log_io,
			LOG_DRAW_TCP_PACKET,
			packet_struct->tcp_header->flags,
			packet_struct->tcp_header->src_port, 
			packet_struct->tcp_header->des_port);
	}

	if(NULL != packet_struct->udp_header)
	{
		fprintf(g_log_io,
			LOG_DRAW_UDP_PACKET,
			packet_struct->udp_header->src_port, 
			packet_struct->udp_header->des_port);
	}

	fprintf(g_log_io, LOG_DRAW_PACKET_END);

}
#endif

//==================================================
// parse function
//==================================================

void parse_packet(pcap_pkthdr *header, u_char *pkt_data)
{		
	def_packet_struct packet_struct = {0};
	
	unsigned int caplen = header->caplen;
	unsigned int lenght = header->len;

	/* packet */
	packet_struct.packet_data     = (def_net_data)pkt_data;
	packet_struct.packet_data_len = caplen;

	/* ether */
	if (caplen < sizeof(def_ether_header)) 
	{		
		return;
	}
	
	memcpy(&packet_struct.ether_header, pkt_data, sizeof(def_ether_header));
	packet_struct.ether_header.type  =  ntohs(packet_struct.ether_header.type);
	caplen                           -= sizeof(def_ether_header);	
	pkt_data                         += sizeof(def_ether_header);
	packet_struct.ether_data         =  (def_net_data)pkt_data;
	packet_struct.ether_data_len     =  caplen;
	
	/* not ehterII */
	if (packet_struct.ether_header.type <= ETHERMTU)
	{
		return;
	}

	/* IP */
	if (packet_struct.ether_header.type == ETHERTYPE_IP)
	{
		/////////////////////////////////////////////////////
		//  Handle IP header
		/////////////////////////////////////////////////////
		if(caplen < sizeof(def_ip_header))
		{
			return;
		}
		memcpy(&packet_struct.ip_header, pkt_data, sizeof(def_ip_header));
		caplen                    -= packet_struct.ip_header.ver_len.len * 4;
		pkt_data                  += packet_struct.ip_header.ver_len.len * 4;
		packet_struct.ip_data     =  (def_net_data)pkt_data;
		packet_struct.ip_data_len =  caplen;

		if(g_map_enable)
		{
			string ipaddr;
			ostringstream strStream;
			def_map_table::iterator iter;		

			
			strStream 
				<< dec
				<< (int)packet_struct.ip_header.saddr.char1 
				<< '.'
				<< (int)packet_struct.ip_header.saddr.char2
				<< '.'
				<< (int)packet_struct.ip_header.saddr.char3 
				<< '.'
				<< (int)packet_struct.ip_header.saddr.char4;
			ipaddr = strStream.str();
			strStream.str("");
			strStream 
				<< hex			
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.saddr.char1
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.saddr.char2
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.saddr.char3
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.saddr.char4
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.saddr.char5
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.saddr.char6;					
			iter = g_map_table.find(ipaddr);			
			if(g_map_table.end() == iter)
			{
				EnterCriticalSection(&critical_table); 					
				g_map_table.insert(def_map_table::value_type(ipaddr, strStream.str()));
				strStream.str("");
				LeaveCriticalSection(&critical_table);
			}
			else
			{				
				EnterCriticalSection(&critical_table); 						
				iter->second = strStream.str();
				strStream.str("");
				LeaveCriticalSection(&critical_table);							
			}

			strStream
				<< dec
				<< (int)packet_struct.ip_header.daddr.char1 
				<< '.'
				<< (int)packet_struct.ip_header.daddr.char2
				<< '.'
				<< (int)packet_struct.ip_header.daddr.char3 
				<< '.'
				<< (int)packet_struct.ip_header.daddr.char4;
			ipaddr = strStream.str();
			strStream.str("");
			strStream 
				<< hex			
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.daddr.char1
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.daddr.char2
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.daddr.char3
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.daddr.char4
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.daddr.char5
				<< ':'
				<< setfill('0') << setw(2) << (int)packet_struct.ether_header.daddr.char6;						
			iter = g_map_table.find(ipaddr);			
			if(g_map_table.end() == iter)
			{
				EnterCriticalSection(&critical_table); 						
				g_map_table.insert(def_map_table::value_type(ipaddr, strStream.str()));
				strStream.str("");	
				LeaveCriticalSection(&critical_table);
			}
			else
			{
				EnterCriticalSection(&critical_table); 					
				iter->second = strStream.str();
				strStream.str("");
				LeaveCriticalSection(&critical_table);
			}
		}


		/////////////////////////////////////////////////////
		//  Handle UDP and TCP
		/////////////////////////////////////////////////////
		switch(packet_struct.ip_header.proto)
		{
		case IPPROTO_UDP:
			if(caplen < sizeof(def_udp_header))
			{
				return;
			}
			memcpy(&packet_struct.udp_header, pkt_data, sizeof(def_udp_header));
			caplen                             -= sizeof(def_udp_header);
			pkt_data                           += sizeof(def_udp_header);		
			packet_struct.udp_header.src_port =  ntohs(packet_struct.udp_header.src_port);
			packet_struct.udp_header.des_port =  ntohs(packet_struct.udp_header.des_port);
			packet_struct.udp_data             =  (def_net_data)pkt_data;
			packet_struct.udp_data_len         =  caplen;
			break;
		case IPPROTO_TCP:
			if(caplen < sizeof(def_tcp_header))
			{
				NM_ERROR("parse_packet, ERROR caplen, caplen=%d\n", caplen);
				return;
			}
			memcpy(&packet_struct.tcp_header, pkt_data, sizeof(def_tcp_header));
			caplen                             -= packet_struct.tcp_header.length.length * 4;
			pkt_data                           += packet_struct.tcp_header.length.length * 4;
			packet_struct.tcp_header.src_port  =  ntohs(packet_struct.tcp_header.src_port);
			packet_struct.tcp_header.des_port  =  ntohs(packet_struct.tcp_header.des_port);
			if(packet_struct.tcp_header.flags.psh)
			{			
				packet_struct.tcp_data     = (def_net_data)pkt_data;
				packet_struct.tcp_data_len =  caplen;
			}
			break;
		default:		
			break;
		}
	}
	else if (packet_struct.ether_header.type == ETHERTYPE_ARP)
	{
		// Handle ARP
	}
	else
	{
		// Can't handle packet except APR and IP
		return;
	}

#if LOG_LEVEL_FIV >= LOG_CUR_LEVEL
	parse_draw_packet(&packet_struct);
#endif
	rule_match(&packet_struct);
	log_flush_io();	
}